package com.lin.shiro.realm;

import com.lin.blog.entity.Permission;
import com.lin.blog.entity.Role;
import com.lin.blog.service.IPermissionService;
import com.lin.blog.service.IRoleService;
import com.lin.constant.RedisConstant;
import com.lin.shiro.pojo.JwtToken;
import com.lin.shiro.utils.JwtUtil;
import com.lin.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
 * @program: blog
 * @description: JWT认证器
 * @author: Lin
 * @create: 2020-04-03 12:28
 **/
@Slf4j
public class JwtRealm extends AuthorizingRealm {

    private static Logger logger = LoggerFactory.getLogger(JwtRealm.class);

    @Autowired
    private IRoleService roleService;

    @Autowired
    private IPermissionService permissionService;


    /**
     * 多重写一个support
     * 标识这个Realm是专门用来验证JwtToken
     * 不负责验证其他的token（UsernamePasswordToken）
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        //这个token就是从过滤器中传入的jwtToken
        return token instanceof JwtToken;
    }

    /**
     * 授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("JwtRealm AuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        JwtUtil jwtUtil = new JwtUtil();


        // 1. 获取jwt
        String jwt = principals.getPrimaryPrincipal().toString();

        // 2.判断jwt是否过期
        checkJwt(jwt, jwtUtil);

        // 3.解析出用户名
        String username = (String) jwtUtil.decode(jwt).get("username");

        // 3.1 通过redis获取
        Set<String> roles = (Set<String>) RedisUtils.get(username + RedisConstant.SHIRO_ROLES_SUFFIX);
        Set<String> permissions = (Set<String>) RedisUtils.get(username +RedisConstant.SHIRO_PERMISSIONS_SUFFIX);
        // 3.2 当两则都不为空时
        if (roles != null && permissions!=null){
            // 3.3. 添加角色与权限
            info.addRoles(roles);
            info.addStringPermissions(permissions);
            logger.info("从缓存中获取成功,无需重新查询");
            return info;
        }

        // 4. 通过用户名获取角色
        List<Role> roleList = roleService.getRoleByUserName(username);

        // 5. 解析出角色
        Set<String> roleSet = new HashSet<>();
        roleList.forEach(role -> {
            roleSet.add(role.getName());
        });

         // 6. 通过角色名获取权限
        Set<String> permissionSet = new HashSet<>();
        roleSet.forEach(roleName->{
            List<Permission> permissionList = permissionService.getPermissionByRoleName(roleName);
            // 7. 解析出权限
            permissionList.forEach(permission -> {
                permissionSet.add(permission.getName());
            });
        });

        // 8. 添加角色与权限
        info.addRoles(roleSet);
        info.addStringPermissions(permissionSet);

        // 9. 存入redis
        RedisUtils.set(username + RedisConstant.SHIRO_ROLES_SUFFIX ,roleSet,RedisConstant.SHIRO_EXPIRE);
        RedisUtils.set(username + RedisConstant.SHIRO_PERMISSIONS_SUFFIX,permissionSet,RedisConstant.SHIRO_EXPIRE);

        logger.info("roleSet {}",roleSet.toString());
        logger.info("permissionSet {}",permissionSet.toString());

        return info;
    }

    /**
     *   认证
     *   这个token就是从过滤器中传入的jwtToken
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        String jwt = (String) token.getPrincipal();
        log.info("jwt:{}",jwt);

        JwtUtil jwtUtil = new JwtUtil();

        // 1.判断jwt是否过期
        checkJwt(jwt, jwtUtil);


        // 2. 获取username
        String username = (String) jwtUtil.decode(jwt).get("username");

        // 3. 判断是否在redis中存在
        String redisToken = RedisUtils.get(username + RedisConstant.JWT_ACCESS_TOKEN_SUFFIX).toString();

        // 3.1 如果redis中存在 则返回正确
        if (jwt.equals(redisToken)){
            return new SimpleAuthenticationInfo(jwt,jwt,"JwtRealm");
        }

        // 4. 从数据库匹配....
        return null;

    }

    /**
     * 检查jwt是否合法
     * @param jwt
     * @param jwtUtil
     */
    private void checkJwt(String jwt, JwtUtil jwtUtil) {
        if (jwt == null) {
            throw new NullPointerException("jwtToken 不允许为空");
        }

        if (!jwtUtil.isVerify(jwt)) {
            throw new UnknownAccountException();
        }
    }


}